package client.ftp;

import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.Set;

import javax.swing.SwingUtilities;

import org.apache.log4j.Logger;

import client.ftp.IFtpClient.HotKey;
import core.AlertUtil;
import core.TextAreaAppender;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import util.FormatUtil;

public class FtpClientController implements Initializable{

	//应用日志对象
	private Logger appLogger;

	/**
	 * 文件最后更新时间格式化字符串
	 */
	private static final String FILE_DATE_FORMAT_STR = "yyyy-MM-dd HH:mm:ss";

	/**
	 * FTP客户端名称与实例的Map集合
	 */
	private static final Map<String,IFtpClient> FTP_CLIENT=new HashMap<String,IFtpClient>();
	static {
		//通过服务发现查找FTP客户端实例
		ServiceLoader<IFtpClient> clients=ServiceLoader.load(IFtpClient.class);
		for(IFtpClient client:clients) {
			FTP_CLIENT.put(client.getName(), client);
		}
	}

	@FXML
	private BorderPane container;

	@FXML
	private TextField localUrlTextField;

	@FXML
	private TableView<LocalFileAttr> localFileTableView;

	@FXML
	private TextField remoteUrlTextField;

	@FXML
	private TableView<RemoteFileAttr> remoteFileTableView;

	/**
	 * 命令历史组件
	 */
	@FXML
	private TextArea cmdTextArea;

	/**
	 * 命令输入框
	 */
	@FXML
	private TextField cmdTextField;

	/**
	 * 日志组件
	 */
	@FXML
	private TextArea logTextArea;

	/**
	 * ftp客户端
	 */
	private IFtpClient ftpClient;

	@Override
	public void initialize(URL location, ResourceBundle resources) {
		appLogger = Logger.getLogger("ftp_client_app");
		appLogger.addAppender(new TextAreaAppender(logTextArea));
		appLogger.info("日志输出初始化完成！");

		/**
		 * 监听窗口关闭，释放资源
		 */
		Platform.runLater(()->{
			Stage stage = (Stage)container.getScene().getWindow();
			 stage.setOnCloseRequest((event)->{
				 if(ftpClient!=null){
					 try {
						ftpClient.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				 }
			 });
		});

		initFileTableView();
	}

	@SuppressWarnings("unchecked")
	private void initFileTableView(){

		//本地表列定义
		TableColumn<LocalFileAttr, String> localFileNameCol=new TableColumn<>("文件名");
		localFileNameCol.setCellValueFactory(new PropertyValueFactory<>("fileName"));
		localFileNameCol.prefWidthProperty().bind(localFileTableView.widthProperty().multiply(0.33));

		TableColumn<LocalFileAttr, String> localFileSizeCol=new TableColumn<>("文件大小");
		localFileSizeCol.setCellValueFactory(new PropertyValueFactory<>("fileSize"));
		localFileSizeCol.prefWidthProperty().bind(localFileTableView.widthProperty().multiply(0.33));

		TableColumn<LocalFileAttr, String> localLastUpdateTimeCol=new TableColumn<>("最后修改");
		localLastUpdateTimeCol.setCellValueFactory(new PropertyValueFactory<>("lastUpdateTime"));
		localLastUpdateTimeCol.prefWidthProperty().bind(localFileTableView.widthProperty().multiply(0.33));

		localFileTableView.getColumns().addAll(localFileNameCol,localFileSizeCol,localLastUpdateTimeCol);

		//远程表列定义
		TableColumn<RemoteFileAttr, String> remoteFileNameCol=new TableColumn<>("文件名");
		remoteFileNameCol.setCellValueFactory(new PropertyValueFactory<>("withFlagFileName"));
		remoteFileNameCol.prefWidthProperty().bind(remoteFileTableView.widthProperty().multiply(0.25));

		TableColumn<RemoteFileAttr, String> remoteFileSizeCol=new TableColumn<>("文件大小");
		remoteFileSizeCol.setCellValueFactory(new PropertyValueFactory<>("fileSize"));
		remoteFileSizeCol.prefWidthProperty().bind(remoteFileTableView.widthProperty().multiply(0.25));

		TableColumn<RemoteFileAttr, String> remoteLastUpdateTimeCol=new TableColumn<>("最后修改");
		remoteLastUpdateTimeCol.setCellValueFactory(new PropertyValueFactory<>("lastUpdateTime"));
		remoteLastUpdateTimeCol.prefWidthProperty().bind(remoteFileTableView.widthProperty().multiply(0.25));

		TableColumn<RemoteFileAttr, String> remotePermissionsStr=new TableColumn<>("权限");
		remotePermissionsStr.setCellValueFactory(new PropertyValueFactory<>("permissionsStr"));
		remotePermissionsStr.prefWidthProperty().bind(remoteFileTableView.widthProperty().multiply(0.25));

		remoteFileTableView.getColumns().addAll(remoteFileNameCol,remoteFileSizeCol,remoteLastUpdateTimeCol,remotePermissionsStr);

		//初始化右键菜单
		initContextMenu();

		//初始化本地文件列表
		initLocalFileView();
	}

	private void initContextMenu() {
		//初始化本地右键菜单
		MenuItem localOpenItem = new MenuItem("打开");
		localOpenItem.setOnAction((event)->{
			LocalFileAttr currItem = localFileTableView.getSelectionModel().getSelectedItem();
			String currItemName = currItem.getFileName();
			String fileName=currItemName.substring(3);
			appLogger.info("[本地]打开 -> "+fileName);
			if(currItemName.startsWith("[D]")){
				try {
					File currDir=new File(localUrlTextField.getText(),fileName);
					localUrlTextField.setText(currDir.getCanonicalPath());
					loadLocalFileList();
				} catch (IOException e) {
					AlertUtil.exception("打开本地目录异常",e);
				}
			}else{
				SwingUtilities.invokeLater(()->{
					try {
						Desktop.getDesktop().open(new File(localUrlTextField.getText(),fileName));
					} catch (IOException e) {
						AlertUtil.exception("打开本地文件异常",e);
					}
				});
			}
		});

		MenuItem uploadItem = new MenuItem("上传");
		uploadItem.setOnAction((event)->{
			LocalFileAttr currItem = localFileTableView.getSelectionModel().getSelectedItem();
			String fileFlagAndName = currItem.getFileName();
			boolean isDir=fileFlagAndName.startsWith("[D]")?true:false;
			String fileName = fileFlagAndName.substring(3);
			appLogger.info("[本地]上传 -> "+fileName);
			try {
				if(ftpClient==null){
					AlertUtil.error("远程未连接！");
					return;
				}
				String filePath = new File(localUrlTextField.getText(),fileName).getCanonicalPath();
				this.upload(filePath,isDir);
			} catch (IOException e) {
				AlertUtil.exception(e);
			}
		});


		MenuItem localDelItem = new MenuItem("删除");
		localDelItem.setOnAction((e)->{
			String fileDir = localUrlTextField.getText();
			LocalFileAttr currItem = localFileTableView.getSelectionModel().getSelectedItem();
			String fileFlagAndName = currItem.getFileName();
			String fileName = fileFlagAndName.substring(3);

			appLogger.info("[本地]删除 -> "+fileName);

			File selectedFile=new File(fileDir,fileName);
			if(selectedFile.delete()){
				loadLocalFileList();
			}
		});

		MenuItem localRefreshItem = new MenuItem("刷新");
		localRefreshItem.setOnAction((e)->{
			appLogger.info("[本地]刷新");
			loadLocalFileList();
		});

		localFileTableView.setContextMenu(new ContextMenu(localOpenItem,uploadItem,localDelItem,localRefreshItem));
		// local contextMenu end

		//初始化远程右键菜单
		MenuItem remoteOpenItem = new MenuItem("打开");
		remoteOpenItem.setOnAction((event)->{
			RemoteFileAttr currItem = remoteFileTableView.getSelectionModel().getSelectedItem();
			String fileName = currItem.getFileName();

			appLogger.info("[远程]打开 -> "+fileName);

			File currDir=new File(remoteUrlTextField.getText(),fileName);
			if(currItem.isDir()){
				try {
					remoteUrlTextField.setText(currDir.getCanonicalPath());
					loadRemoteFileList();
				} catch (IOException e) {
					AlertUtil.exception("打开远程目录异常", e);
				}
			}else{
				try{
					//远程文件下载到本地，然后使用本地程序打开
					if(ftpClient==null){
						AlertUtil.error("远程未连接！");
						return;
					}
					File temp = File.createTempFile(fileName.substring(0,fileName.lastIndexOf('.')), fileName.substring(fileName.lastIndexOf('.')));
					ftpClient.download(currDir.getCanonicalPath(),temp.getCanonicalPath());
					SwingUtilities.invokeLater(()->{
						try {
							Desktop.getDesktop().open(temp);
						} catch (IOException e) {
							AlertUtil.exception("打开远程文件异常", e);
						}
					});
				}catch(IOException e){
					e.printStackTrace();
				}
			}
		});

		MenuItem downloadItem = new MenuItem("下载");
		downloadItem.setOnAction((event)->{
			RemoteFileAttr currItem = remoteFileTableView.getSelectionModel().getSelectedItem();
			String fileName = currItem.getFileName();

			appLogger.info("[远程]下载 -> "+fileName);

			boolean isDir = currItem.isDir();
			try {
				if(ftpClient==null){
					AlertUtil.error("远程未连接！");
					return;
				}
				String remoteFilePath = new File(remoteUrlTextField.getText(),fileName).getCanonicalPath();
				this.download(remoteFilePath,isDir);
			} catch (IOException e) {
				AlertUtil.exception(e);
			}
		});

		MenuItem remoteDelItem = new MenuItem("删除");
		remoteDelItem.setOnAction((event)->{
			RemoteFileAttr currItem = remoteFileTableView.getSelectionModel().getSelectedItem();
			String fileName = currItem.getFileName();
			appLogger.info("[远程]删除 -> "+fileName);
			try {
				if(ftpClient==null){
					AlertUtil.error("远程未连接！");
					return;
				}
				ftpClient.delFile(new File(remoteUrlTextField.getText(),fileName).getCanonicalPath(),currItem.isDir());
				loadRemoteFileList();
			} catch (IOException e) {
				AlertUtil.exception(e);
			}
		});

		MenuItem remoteRefreshItem = new MenuItem("刷新");
		remoteRefreshItem.setOnAction((e)->{
			loadRemoteFileList();
		});

		remoteFileTableView.setContextMenu(new ContextMenu(remoteOpenItem,downloadItem,remoteDelItem,remoteRefreshItem));
		// remote contextMenu end
	}

	private void download(String remoteFilePath,boolean isDir){
		String localDir = localUrlTextField.getText();
		this.download(remoteFilePath,localDir,isDir);
		loadLocalFileList();
	}

	private void download(String remoteFilePath,String localDir,boolean isDir){
		try {
			if(ftpClient==null){
				AlertUtil.error("远程未连接！");
				return;
			}

			File remoteFile=new File(remoteFilePath);
			File localFile=new File(localDir,remoteFile.getName());
			if(isDir){
				String newLocalDir = localFile.getCanonicalPath();
				List<RemoteFileAttr> fileList = ftpClient.listFile(remoteFilePath);
				for(RemoteFileAttr fileEntry:fileList){
					String filePath=new File(remoteFilePath,fileEntry.getFileName()).getCanonicalPath();
					download(newLocalDir,filePath,fileEntry.isDir());
				}
			}else{
				String localFilePath=localFile.getCanonicalPath();
				appLogger.info("[下载文件] "+remoteFilePath+" -> "+localFilePath);
				ftpClient.download(remoteFilePath, localFilePath);
			}
		} catch (IOException e) {
			AlertUtil.exception(e);
		}
	}

	private void upload(String localFilePath, boolean isDir) {
		String remoteDir=remoteUrlTextField.getText();
		this.upload(localFilePath,remoteDir,isDir);
		loadRemoteFileList();
	}

	private void upload(String localFilePath, String remoteDir, boolean isDir) {
		try {
			File localFile=new File(localFilePath);
			File remoteFile=new File(remoteDir,localFile.getName());
			if(isDir){
				File[] localFiles=localFile.listFiles();
				String newRemoteDir=remoteFile.getCanonicalPath();
				for(File file:localFiles){
					upload(file.getCanonicalPath(),newRemoteDir,file.isDirectory());
				}
			}else{
				String remoteFilePath = remoteFile.getCanonicalPath();
				appLogger.info("[上传文件] "+localFilePath+" -> "+remoteFilePath);
				if(ftpClient==null){
					AlertUtil.error("远程未连接！");
					return;
				}
				ftpClient.upload(localFilePath, remoteFilePath);
			}
		} catch (IOException e) {
			AlertUtil.exception(e);
		}
	}

	private void initLocalFileView() {
		/**
		 * 本地路径默认使用用户家目录
		 */
		String userHome = System.getProperty("user.home");
		localUrlTextField.setText(userHome);
		loadLocalFileList();

		/**
		 * 本地地址输入完成事件
		 */
		localUrlTextField.setOnAction((value)->{
			loadLocalFileList();
		});

		/**
		 * 监听文件双击事件
		 */
		localFileTableView.setOnMouseClicked((MouseEvent event)->{
			if(event.getClickCount()==2 && localFileTableView.getSelectionModel().getSelectedItem()!=null){
				LocalFileAttr currItem = localFileTableView.getSelectionModel().getSelectedItem();
				String currItemName = currItem.getFileName();
				String fileName=currItemName.substring(3);
				appLogger.info("[本地]打开 -> "+fileName);
				if(currItemName.startsWith("[D]")){
					try {
						File currDir=new File(localUrlTextField.getText(),fileName);
						localUrlTextField.setText(currDir.getCanonicalPath());
						loadLocalFileList();
					} catch (IOException e) {
						AlertUtil.exception("打开本地目录异常",e);
					}
				}else{
					SwingUtilities.invokeLater(()->{
						try {
							Desktop.getDesktop().open(new File(localUrlTextField.getText(),fileName));
						} catch (IOException e) {
							AlertUtil.exception("打开本地文件异常",e);
						}
					});
				}
			}
		});
	}

	/**
	 * 加载本地文件列表
	 */
	private void loadLocalFileList(){
		String filePath = localUrlTextField.getText();
		File userHomeDir=new File(filePath);
		File[] files = userHomeDir.listFiles();
		localFileTableView.getItems().clear();
		localFileTableView.getItems().add(new LocalFileAttr("[D]..","",""));//添加一个导航到父级的节点
		for(int i=0;i<files.length;i++){
			String fileFlag = files[i].isDirectory()?"[D]":"[F]";
			String fileName = files[i].getName();
			String fileSize = files[i].isFile()?FormatUtil.formatFileSize(files[i].length()):"";
			String lastUpdateTime = FormatUtil.formatDate(files[i].lastModified(),FILE_DATE_FORMAT_STR);
			localFileTableView.getItems().add(new LocalFileAttr(fileFlag+fileName,fileSize,lastUpdateTime));
		}
	}

	private void initRemoteView() {
		if(ftpClient==null){
			AlertUtil.error("远程未连接！");
			return;
		}
		remoteUrlTextField.setText(ftpClient.pwd());
		loadRemoteFileList();

		/**
		 * 远程地址输入完成事件
		 */
		remoteUrlTextField.setOnAction((value)->{
			loadRemoteFileList();
		});

		/**
		 * 监听双击事件
		 */
		remoteFileTableView.setOnMouseClicked((MouseEvent event)->{
			if(event.getClickCount()==2 && remoteFileTableView.getSelectionModel().getSelectedItem()!=null){
				RemoteFileAttr currItem = remoteFileTableView.getSelectionModel().getSelectedItem();
				String fileName = currItem.getFileName();
				appLogger.info("[远程]打开 -> "+fileName);
				File currDir=new File(remoteUrlTextField.getText(),fileName);
				if(currItem.isDir()){
					try {
						remoteUrlTextField.setText(currDir.getCanonicalPath());
						loadRemoteFileList();
					} catch (IOException e) {
						AlertUtil.exception(e);
					}
				}else{
					try{
						if(ftpClient==null){
							AlertUtil.error("远程未连接！");
							return;
						}
						File temp = File.createTempFile(fileName.substring(0,fileName.lastIndexOf('.')), fileName.substring(fileName.lastIndexOf('.')));
						ftpClient.download(currDir.getCanonicalPath(), temp.getCanonicalPath());
						SwingUtilities.invokeLater(()->{
							try {
								Desktop.getDesktop().open(temp);
							} catch (IOException e) {
								AlertUtil.exception(e);
							}
						});
					}catch(IOException e){
						AlertUtil.exception(e);
					}
				}
			}
		});
	}

	private void loadRemoteFileList(){
		if(ftpClient==null){
			AlertUtil.error("远程未连接！");
			return;
		}
		String remoteFilePath=remoteUrlTextField.getText();
		List<RemoteFileAttr> fileList = ftpClient.listFile(remoteFilePath);
		remoteFileTableView.getItems().clear();
		remoteFileTableView.getItems().add(new RemoteFileAttr("..","","","",true));//添加一个导航到父级的节点
		remoteFileTableView.getItems().addAll(fileList);
	}

	private IFtpClient choiceClient() {
		appLogger.info("进行客户端选择。");
		if(FTP_CLIENT.size() == 1) {
			appLogger.info("仅有一个客户端示例，无需选择！");
			//只有一种客户端
			for(IFtpClient client:FTP_CLIENT.values()) {
				return client;
			}
		}

		appLogger.info("启动客户端选择对话框。");
		//弹出选择对话框进行选择
		Set<String> clients = FTP_CLIENT.keySet();
		Optional<String> result = AlertUtil.choice("选择客户端", "请选择连接客户端类型：", clients);
		if(result.isPresent()) {
			appLogger.info("用户选择客户端："+result.get());
			return FTP_CLIENT.get(result.get());
		}

		return null;
	}

	/**
	 * 新建连接按钮监听
	 * @param e	事件对象
	 * @throws IOException　Ftp连接操作可能发生异常
	 */
	public void createConnection(ActionEvent e) throws IOException{
		appLogger.info("检查是否存在已连接的客户端。");
		//如果FTP客户端实例不为空则先关闭之前的连接
		if(ftpClient != null) {
			appLogger.info("关闭已连接的客户端。");
			ftpClient.close();
		}

		//选择FTP客户端
		this.ftpClient = this.choiceClient();
		if(ftpClient != null) {
			//初始化FTP连接和组件
			boolean connected=ftpClient.init((String data)->{
				cmdTextArea.appendText(data);
				cmdTextArea.setScrollTop(Double.MIN_VALUE);
			});
			if(connected){
				initRemoteView();
			}
		}
	}

	/**
	 * 执行命令动作监听
	 * @param e　事件对象
	 */
	public void exeCmd(ActionEvent e) {
		if(ftpClient == null) {
			AlertUtil.error("远程未连接");
			return;
		}

		String cmdStr = cmdTextField.getText();
		if(cmdStr == null || "".equals(cmdStr.trim())) {
			appLogger.error("无法执行用户输入命令！【命令为空】");
			return;
		}

		appLogger.info("执行用户输入命令："+cmdStr);
		//执行ftp命令
		ftpClient.exeCmd(cmdStr);
		//清空命令输入框
		cmdTextField.clear();
	}

	/**
	 * 发送快捷键
	 * @param e
	 */
	public void sendHotKey(ActionEvent e){
		if(ftpClient == null) {
			AlertUtil.error("远程未连接");
			return;
		}

		//弹出选择对话框进行快捷键选择
		Map<String,HotKey> hotMap = new HashMap<>();
		for(HotKey hotKey:HotKey.values()){
			hotMap.put(hotKey.getShowName(), hotKey);
		}
		Optional<String> result = AlertUtil.choice("选择快捷键", "选择将要发送给服务端的快捷键：", hotMap.keySet());
		if(result.isPresent()){
			String hotKeyName=result.get();
			appLogger.info("发送快捷键 -> " +hotKeyName);
			ftpClient.sendHotKey(hotMap.get(hotKeyName));
		}
	}
}
